﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.ComponentModel;
using System.Collections.Specialized;
using System.Text;
using System.Reflection;
using System.Linq;

namespace Bling.Util {
  public interface ICanStart {
    void Start();
    void Stop();
  }
  public interface IHasGlobalPosition {
    void SetGlobalPosition(Bling.Core.Double2Bl Point);
    void UnsetGlobalPosition();
  }


  public struct IntPoint {
    public int X;
    public int Y;

  }

  public static class MyMath {
    public static long Factorial(int n) {
      if (n <= 0) return 1;
      else return n * Factorial(n - 1);
    }
    private static readonly long[] FactTable = new long[20];
    static MyMath() {
      Func<int,long> Fact = null;
      int j = 0;
      Fact = (i) => {
        if (i <= 1) return 1;
        else if (i < j) return FactTable[j];
        else return i * Fact(i - 1);
      };
      for (j = 0; j < FactTable.Length; j++) FactTable[j] = Fact(j);
    }
  }

  public class MockArray<INDEX, ITEM> {
    public Func<INDEX, ITEM> Get { get; set; }
    public Action<INDEX, ITEM> Set { get; set; }
    public int Count;
    public ITEM this[INDEX idx] {
      get { return Get(idx); }
      set { Set(idx, value); }
    }
  }
  public interface ISimpleList<T> : IEnumerable<T> {
    T this[int index] { get; }
    int Count { get; }
    int IndexOf(T item);
    bool Contains(T item);
  }
  public abstract class SimpleList<T> : ISimpleList<T> {
    public abstract int Count { get; }
    public abstract T this[int index] { get; }
    public abstract int IndexOf(T item);
    public abstract bool Contains(T item);

    public abstract IEnumerator<T> GetEnumerator();
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); }

    public static implicit operator SimpleList<T>(T[] list) { return new ListToSimple<T>(list); }
    public static implicit operator SimpleList<T>(List<T>list) { return new ListToSimple<T>(list); }
  }

  public class ListToSimple<T> : SimpleList<T> {
    readonly IList<T> Underlying;
    public ListToSimple(IList<T> Underlying) { this.Underlying = Underlying; }
    public override int Count {
      get { return Underlying.Count; }
    }
    public override IEnumerator<T> GetEnumerator() {
      return Underlying.GetEnumerator();
    }
    public override T this[int index] {
      get { return Underlying[index]; }
    }
    public override bool Contains(T item) {
      return Underlying.Contains(item);
    }
    public override int IndexOf(T item) {
      return Underlying.IndexOf(item);
    }
    public override string ToString() {
      return Underlying.ToString();
    }
  }
  public interface IListExt<S> : IList<S> {
    IListExt<Q> Map<Q>(Func<S, Q> F);
  }
  public class ConcatList<T> : IListExt<T> {
    readonly IList<T> UnderlyingA;
    readonly IList<T> UnderlyingB;
    public ConcatList(IList<T> UnderlyingA, IList<T> UnderlyingB) {
      this.UnderlyingA = UnderlyingA;
      this.UnderlyingB = UnderlyingB;
    }
    public T this[int index] {
      get { return index < UnderlyingA.Count ? UnderlyingA[index] : UnderlyingB[index - UnderlyingA.Count]; }
      set {
        if (index < UnderlyingA.Count) UnderlyingA[index] = value;
        else UnderlyingB[index - UnderlyingA.Count] = value;
      }
    }
    public int Count { get { return UnderlyingA.Count + UnderlyingB.Count; } }
    public bool Contains(T item) {
      return UnderlyingA.Contains(item) || UnderlyingB.Contains(item);
    }
    public int IndexOf(T item) {
      var ret = UnderlyingA.IndexOf(item);
      if (ret < 0) {
        ret = UnderlyingB.IndexOf(item);
        if (ret >= 0) ret = ret + UnderlyingA.Count;
      }
      return ret;
    }
    public IEnumerator<T> GetEnumerator() {
      return ((IEnumerable<T>) UnderlyingA).Concat(UnderlyingB).GetEnumerator();
    }
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); }
    public void Insert(int index, T item) {
      if (index <= UnderlyingA.Count) UnderlyingA.Insert(index, item);
      else UnderlyingB.Insert(index - UnderlyingA.Count, item);
    }
    public void RemoveAt(int index) {
      if (index < UnderlyingA.Count) UnderlyingA.RemoveAt(index);
      else UnderlyingB.RemoveAt(index - UnderlyingA.Count);
    }
    public bool IsReadOnly { get { return UnderlyingA.IsReadOnly || UnderlyingB.IsReadOnly; } }
    public void Add(T item) {
      UnderlyingB.Add(item);
    }
    public void Clear() { UnderlyingA.Clear(); UnderlyingB.Clear(); }
    public void CopyTo(T[] array, int arrayIndex) {
      for (int i = 0; i < Count; i++)
        array[arrayIndex + i] = this[i];
    }
    public bool Remove(T item) {
      return UnderlyingA.Remove(item) || UnderlyingB.Remove(item);
    }
    public IListExt<Q> Map<Q>(Func<T, Q> F) {
      return new ConcatList<Q>(UnderlyingA.Map(F), UnderlyingB.Map(F));
    }

  }

  public class MapList<T, S> : IListExt<S> {
    readonly IList<T> Underlying;
    readonly Func<T, S> F;
    readonly Func<S, T> G;
    public MapList(IList<T> Underlying, Func<T, S> F, Func<S,T> G) {
      this.Underlying = Underlying;
      this.F = F;
      this.G = G;
    }
    public MapList(IList<T> Underlying, Func<T, S> F) : this(Underlying, F, null) {}

    public IListExt<Q> Map<Q>(Func<S, Q> F) {
      return new MapList<T, Q>(Underlying, x => F(this.F(x)));

    }

    public S this[int index] { 
      get { return F(Underlying[index]); }
      set {
        if (G != null) Underlying[index] = G(value);
        else throw new NotSupportedException();
      }
    }
    public int Count { get { return Underlying.Count; } }
    public bool Contains(S item) {
      if (G != null) return Underlying.Contains(G(item));
      foreach (var i in Underlying) 
        if (F(i).Equals(item)) return true;
      return false;
    }
    public int IndexOf(S item) {
      if (G != null) return Underlying.IndexOf(G(item));
      for (int i = 0; i < Underlying.Count; i++)
        if (F(Underlying[i]).Equals(item)) return i;
      return -1;
    }
    public IEnumerator<S> GetEnumerator() {
      return Underlying.Select<T, S>(F).GetEnumerator();
    }
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); }
    public void Insert(int index, S item) {
      if (G != null) {
        Underlying.Insert(index, G(item));
        return;
      }
      throw new NotSupportedException();
    }
    public void RemoveAt(int index) { Underlying.RemoveAt(index); }
    public bool IsReadOnly { get { return Underlying.IsReadOnly && G != null; } }
    public void Add(S item) {
      if (G != null) Underlying.Add(G(item));
      else throw new NotSupportedException();
    }
    public void Clear() { Underlying.Clear(); }
    public void CopyTo(S[] array, int arrayIndex) {
      for (int i = 0; i < Count; i++)
        array[arrayIndex + i] = this[i];
    }
    public bool Remove(S item) {
      if (G != null) return Underlying.Remove(G(item));
      else throw new NotSupportedException();
    }

  }
  // useful....
  public interface ICell<T> {
    T Value { get; set; }
  }
  public interface ICell<S, T> {
    T this[S Container] { get; set; }
  }

  public class Cell {
    public class Key { }
  }
  public interface IHasCells {
    Dictionary<Cell.Key, Cell> CellBank { get; }
  }

  public class Cell<T> : Cell, ICell<T> {
    public T Value { get; set; }
    public class Observable : Cell<T> {
      private List<Func<bool>> Observers;
      public void Register(Func<bool> obs) { Observers = Observers.Register(obs); }
      public void Flush() { Observers.Flush(); }
      public new class Key : Cell<T>.Key {
        protected override Cell<T> Create() { return new Observable(); }
      }
    }
    // unlocks a cell
    public new class Key : Cell.Key {
      protected virtual Cell<T> Create() { return new Cell<T>(); }
      public Cell<T> this[IHasCells Bank] { get { return this[Bank.CellBank]; } }
      public Cell<T> this[Dictionary<Cell.Key, Cell> Bank] {
        get {
          if (!Bank.ContainsKey(this)) Bank[this] = Create();
          return (Cell<T>) Bank[this];
        }
      }
    }
  }




  public interface ICellContainer {
    object[] CellData { get; }
  }
  public interface IHasCells<S> : IHasCells where S : ICellContainer {
  }
  public interface ICellWithIndex { int Index { get; } }
  public interface ICellKey { }
  public interface ICellKey<T> : ICellKey { }
  public class DefaultCellKey<T> : ICellKey<T> { }

  public interface ICellKey<S, T> : ICellKey<T> where S : ICellContainer {
    ICell<S, T> this[IHasCells<S> Bank] { get; }
  }
  public class DefaultCellKey<S, T, X> : ICellKey<S, T> where S : ICellContainer {
    public ICellKey<S, X> Core;
    public Func<X, T> Get;
    public Action<X, T> Set;
    private class Cell : ICell<S, T> {
      public Func<S, T> Get;
      public Action<S, T> Set;
      public T this[S Container] {
        get { return Get(Container); }
        set { Set(Container, value); }
      }
    }

    public ICell<S, T> this[IHasCells<S> Bank] {
      get {
        var core0 = Core[Bank];
        return new Cell() {
          Get = (s) => Get(core0[s]),
          Set = (s,value) => Set(core0[s], value),

        };
      }
    }

  }

  public class DefaultCell<S, T, X> : ICell<S, T>, ICellKey<S,T> where S : ICellContainer {
    public Func<S, X> F;
    public Func<X, T> Get;
    public Action<X, T> Set;
    public T this[S Container] {
      get { return Get(F(Container)); }
      set { Set(F(Container), value); }
    }
    ICell<S, T> ICellKey<S, T>.this[IHasCells<S> Bank] { get { return this; } }
  }

  public class Cell<S, T> : Cell, ICell<S, T>, ICellWithIndex where S : ICellContainer {
    private readonly int Index;
    int ICellWithIndex.Index { get { return Index; } }
    protected Cell(int Index) { this.Index = Index; }
    public T this[S Container] {
      get { return (T)Container.CellData[Index]; }
      set { Container.CellData[Index] = value; }
    }
    public new class Key : Cell.Key, ICellKey<S, T> {
      protected virtual Cell<S,T> Create(int Index) { return new Cell<S,T>(Index); }
      public Cell<S, T> this[IHasCells<S> Bank] { get { return this[Bank.CellBank]; } }
      ICell<S, T> ICellKey<S,T>.this[IHasCells<S> Bank] { get { return this[Bank]; } }
      public Cell<S, T> this[Dictionary<Cell.Key, Cell> Bank] {
        get {
          if (!Bank.ContainsKey(this)) {
            Bank[this] = Create(Bank.Count);
          }
          return (Cell<S,T>)Bank[this];
        }
      }
    }
  }

  public static class Extensions {

    public static List<T> AddInit<T>(this List<T> List, T Value) {
      if (List == null) List = new List<T>();
      List.Add(Value);
      return List;
    }
    public static HashSet<T> AddInit<T>(this HashSet<T> Set, T Value) {
      if (Set == null) Set = new HashSet<T>();
      Set.Add(Value);
      return Set;
    }
    public static bool ContainsIfNotNull<T>(this HashSet<T> Set, T Value) {
      return Set != null && Set.Contains(Value);
    }

    public static IEnumerable<T> Unique<T>(this IEnumerable<T> E) {
      var Set = new HashSet<T>();
      foreach (var e in E)
        if (e != null && Set.Add(e)) yield return e;
    }
    public static IEnumerable<T> TakeAtMost<T>(this IEnumerable<T> E, int N) {
      foreach (var e in E) {
        if (N == 0) yield break;
        yield return e;
        N -= 1;
      }

    }

    public static bool MoveNextNoNull<T>(this IEnumerator<T> E) {
      if (!E.MoveNext()) return false;
      while (E.Current == null) if (!E.MoveNext()) return false;
      return true;
    }
    public static IEnumerable<T> WhereT<S, T>(this IEnumerable<S> Enum, Func<S,T> F) where T : S {
      return Enum.Where(x => x is T).Select(x => F(x));
    }
    public static string Format<A>(this IEnumerable<A> List, Func<A, string> F = null, string delim = ", ") {
      if (F == null) F = a => a.ToString();
      return List.Aggregate("", (s, a) => {
        if (s.Length > 0) s = s + delim;
        return s + F(a);
      });
    }

    public static void PushTranslate<A, B>(this Dictionary<A, B> Dict, B Key, B Value) where A : B {
      B Key0 = Key;
      while (Key0 is A) {
        var Key1 = (A) Key0;
        if (Dict.ContainsKey(Key1)) {
          Key0 = Dict[Key1]; Dict[Key1] = Value;
        } else {
          Dict[Key1] = Value; break;
        }
      }
    }

    public static B Translate<A, B>(this Dictionary<A, B> Dict, B Key) where A : B {
      B Key0 = Key;
      while (Key0 is A && Dict.ContainsKey((A)Key0)) Key0 = Dict[(A)Key0];
      return Key0;
    }

    public static void Put<K, C, E>(this Dictionary<K, C> Dict, K Key, E Element) where C : ICollection<E>, new() {
      (!Key.Equals(Element)).Assert();
      if (!Dict.ContainsKey(Key)) Dict[Key] = new C();
      Dict[Key].Add(Element);
    }
    /*
    public static IEnumerable<E> Get<K, C, E>(this Dictionary<K, C> Dict, K Key) where C : ICollection<E> {
      if (!Dict.ContainsKey(Key)) return new E[0];
      return Dict[Key];
    }*/
    public static IEnumerable<E> Get<K, E>(this Dictionary<K, HashSet<E>> Dict, K Key)  {
      if (!Dict.ContainsKey(Key)) return new E[0];
      return Dict[Key];
    }
    public static int Count<K>(this Dictionary<K, int> Dict, K Key) {
      if (!Dict.ContainsKey(Key)) Dict[Key] = Dict.Count;
      return Dict[Key];
    }
    public static bool TestAndSet(this bool[] List, int Index) {
      if (List[Index]) return false;
      List[Index] = true;
      return true;
    }

    public static Dictionary<B, C> Get<A, B, C>(this Dictionary<A, Dictionary<B, C>> Dict, A Key) {
      if (!Dict.ContainsKey(Key)) Dict[Key] = new Dictionary<B,C>();
      return Dict[Key];
    }


    public static ICellKey<S, T> Prop<S, T, X>(this ICellKey<S, X> Key, Func<X, T> Get, Action<X, T> Set) where S : ICellContainer {
      return new DefaultCellKey<S, T, X>() { Core = Key, Get = Get, Set = Set };
    }
    public static bool Set<S>(this ICell<S, bool> Cell, S Actual) where S : ICellContainer {
      if (Cell[Actual]) return false;
      Cell[Actual] = true;
      return true;
    }
    /*
    public static ICell<S, T> Merge<S, T>(this ICell<S, T> Cell, Func<S, S> F) where S : ICellContainer {

    }*/
    public static bool DeepEquals<T>(this IList<T> ListA, IList<T> ListB) {
      if (ListA.Count != ListB.Count) return false;
      for (int i = 0; i < ListA.Count; i += 1) if (!ListA[i].Equals(ListB[i])) return false;
      return true;
    }
    public static int DeepHashCode<T>(this IList<T> ListA) {
      var hc = 0;
      for (int i = 0; i < ListA.Count; i += 1) hc += ListA[i].GetHashCode();
      return hc;
    }



    public static IListExt<S> Weaken<T, S>(this IList<T> List) where T : S {
      return new MapList<T, S>(List, e => e, e => (T) e);
    }

    public static IListExt<S> Map<T, S>(this IList<T> List, Func<T, S> F) {
      if (List is IListExt<T>) return ((IListExt<T>)List).Map<S>(F);
      return new MapList<T, S>(List, F);
    }
    public static T Find<T>(this IList<T> List, Predicate<T> Predicate) {
      foreach (var e in List) if (Predicate(e)) return e;
      return default(T);
    }

    public static MapList<T, S> Map<T, S>(this IList<T> List, Func<T, S> F, Func<S,T> G) {
      return new MapList<T, S>(List, F, G);
    }
    public static void AddIfNotNull<T>(this IList<T> List, T t) {
      if (t != null) List.Add(t);
    }
    public static bool ContainsAll<T>(this HashSet<T> Set, HashSet<T> Other) {
      foreach (var o in Other) if (!Set.Contains(o)) return false;
      return true;
    }

    public static IList<T> Concat<T>(this IList<T> List, IList<T> Other) {
      if (Other == null) return List;
      else if (Other.Count == 0) return List;
      else if (List == null || List.Count == 0) return Other;
      return new ConcatList<T>(List, Other);
    }


    public static List<Action> Flush(this List<Action> List) {
      if (List != null && List.Count > 0) {
        var List0 = List.ToArray();
        List.Clear();
        foreach (var l in List0) l();
        if (List.Count == 0) return null;
        else return List;
      } else return null;
    }
    public static void Flush(this List<Func<bool>> List) {
      if (List != null && List.Count > 0) {
        var List0 = List.ToArray();
        List.Clear();
        foreach (var l in List0) if (l()) List.Add(l);
      }
    }
    public static List<Action<A>> Flush<A>(this List<Action<A>> List, A a) {
      if (List != null && List.Count > 0) {
        var List0 = List.ToArray();
        List.Clear();
        foreach (var l in List0) l(a);
        return (List.Count == 0 ? null : List);
      }
      return null;
    }
    public static void Flush<A>(this List<Func<A,bool>> List, A a) {
      if (List != null && List.Count > 0) {
        var List0 = List.ToArray();
        List.Clear();
        foreach (var l in List0) if (l(a)) List.Add(l);
      }
    }
    public static void Flush<A, B>(this List<Action<A, B>> List, A a, B b) {
      if (List != null && List.Count > 0) {
        var List0 = List.ToArray();
        List.Clear();
        foreach (var l in List0) l(a, b);
      }
    }
    public static HashSet<T> Register<T>(this HashSet<T> Set, T t) {
      if (object.ReferenceEquals(t, null)) return Set;
      if (Set == null) Set = new HashSet<T>();
      Set.Add(t);
      return Set;
    }
    public static HashSet<T> Flush<T>(this HashSet<T> Set, Action<T> A) {
      if (Set == null) return null;
      foreach (var t in Set) A(t);
      return null;
    }

    public static List<T> Register<T>(this List<T> List, T t) {
      if (List == null) List = new List<T>();
      List.Add(t);
      return List;
    }


    public static void Flush<A, B>(this List<Func<A, B, bool>> List, A a, B b) {
      if (List != null && List.Count > 0) {
        var List0 = List.ToArray();
        List.Clear();
        foreach (var l in List0) if (l(a, b)) List.Add(l);
      }
    }
    public static List<Action<A, B, C>> Flush<A, B, C>(this List<Action<A, B, C>> List, A a, B b, C c) {
      if (List != null && List.Count > 0) {
        var List0 = List.ToArray();
        List.Clear();
        foreach (var l in List0) l(a, b, c);
        return List.Count == 0 ? null : List;
      }
      return null;
    }
    public static void Flush<A, B, C>(this List<Func<A, B, C, bool>> List, A a, B b, C c) {
      if (List != null && List.Count > 0) {
        var List0 = List.ToArray();
        List.Clear();
        foreach (var l in List0) if (l(a, b, c)) List.Add(l);
      }
    }
    //public static FuncBl<A, B> Bl<A, B>(this Func<A, B> F) { return new FuncBl<A, B>() { F = F }; }
    //public static FuncBl<A, B, C> Bl<A, B, C>(this Func<A, B, C> F) { return new FuncBl<A, B, C>() { F = F }; }
    private static void GetAllProperties(this Type Type, List<PropertyInfo> Properties) {
      foreach (var p in Type.GetProperties()) Properties.Add(p);
      foreach (var t in Type.GetInterfaces()) GetAllProperties(t, Properties);
    }

    public static IList<PropertyInfo> GetAllProperties(this Type Type) {
      var list = new List<PropertyInfo>();
      Type.GetAllProperties(list);
      return list;
    }
    public static void Trace(string str) {

    }
    public static Action Concat(this Action a0, params Action[] a1) {
      return () => {
        a0();
        foreach (var a in a1) a();
      };
    }
    public static Action<T> Concat<T>(this Action<T> a0, params Action<T>[] a1) {
      return (t) => {
        a0(t);
        foreach (var a in a1) a(t);
      };
    }
    public static bool Exists<T>(this IEnumerable<T> E, Func<T, bool> P) {
      foreach (var e in E) if (P(e)) return true;
      return false;
    }
    public static bool IsEmpty<T>(this IEnumerable<T> E) {
      return !E.GetEnumerator().MoveNextNoNull();
    }
    public static IEnumerable<T> Prepend<T>(this IEnumerable<T> E, T Value) {
      yield return Value;
      foreach (var e in E) yield return e;
    }

    public static void Assert(this bool value) {
      if (!value) {
        Debug.Assert(false);
      }
    }
    /*
    public static void CheckInit(this object value) {
    }
    public static void CheckInit(this Type value) {
      value.TypeInitializer.Invoke(new object[] {});
    }
     */
    public static void CheckInit<T>() {
      var m = typeof(T).GetMethod("CheckInit", new Type[] { });
      if (m != null) m.Invoke(null, new object[] { });
    }

    public static T Convert<T>(this string str) {
      return (T)TypeDescriptor.GetConverter(typeof(T)).ConvertFrom(str);
    }

    public static Uri MakePackUri(this string relativeFile, Type From) {
      StringBuilder uriString = new StringBuilder();
      uriString.Append("pack://application:,,,");
      uriString.Append("/" + AssemblyShortName(From) + ";component/" + relativeFile);
      return new Uri(uriString.ToString(), UriKind.RelativeOrAbsolute);
    }
    public static System.IO.Stream FromAssembly(this string relativeFile, Type From) {
      Assembly a = From.Assembly;
      return a.GetFile(relativeFile);
      //var names = a.GetManifestResourceNames();

      //return a.GetManifestResourceStream(relativeFile);
    }


    private static string AssemblyShortName(Type From) {
      Assembly a = From.Assembly;

      // Pull out the short name.
      return a.ToString().Split(',')[0];
    }
    /// <summary>
    /// Add multiple elements to a list.
    /// </summary>
    public static void Add<T>(this IList<T> list, params T[] Elems) {
      foreach (var e in Elems) list.Add(e);
    }

    public static double Clamp(this double at, double min, double max) {
      if (at < min) at = min;
      if (at > max) at = max;
      return at;
    }
    public static float Clamp(this float at, float min, float max) {
      if (at < min) at = min;
      if (at > max) at = max;
      return at;
    }
    public static int Clamp(this int at, int min, int max) {
      if (at < min) at = min;
      if (at > max) at = max;
      return at;
    }
    public static bool IsNan(this double d) {
      return double.IsNaN(d);
    }

    public static double Square(this double t) { return t * t; }
    public static double Pow(this double t, double e) { return Math.Pow(t, e); }
    public static double Sqrt(this double t) { return Math.Sqrt(t); }



    public static T[] ToArray<T>(this MockArray<int, T> array) {
      var ret = new T[array.Count];
      for (int i = 0; i < array.Count; i++)
        ret[i] = array.Get(i);
      return ret;
    }
  }
}

namespace Bling.Mixins {
  using Bling.Util;
  using System.Reflection;
  public interface IHasMixin {
    //void DoInit();
  }
  public interface IHasMixin<TARGET> : IHasMixin where TARGET : IHasMixin<TARGET> {
    MixinManager<TARGET> Mixins { get; }
  }
  public interface IHasMixin<TARGET, MIXIN> : IHasMixin<TARGET>
    where TARGET : IHasMixin<TARGET, MIXIN>
    where MIXIN : IMixin<TARGET, MIXIN> {
  }
  public interface IMixin { }
  public interface IMixin<TARGET> : IMixin where TARGET : IHasMixin<TARGET> {
    void DoInit(TARGET block);
  }
  public interface IMixin<TARGET, MIXIN> : IMixin<TARGET>
    where TARGET : IHasMixin<TARGET, MIXIN>
    where MIXIN : IMixin<TARGET, MIXIN> {
  }
  public abstract class Mixin : IMixin {
    internal Mixin() { }
  }
  public abstract class Mixin<TARGET> : Mixin, IMixin<TARGET> where TARGET : IHasMixin<TARGET> {
    public readonly TARGET Target;
    protected internal void ForceInit<MIXIN>(TARGET Target) where MIXIN : Mixin<TARGET> {
      Target.Mixins.ForceInit(Target, typeof(MIXIN));
    }
    public abstract void DoInit(TARGET Target);
    internal Mixin(TARGET Target) { this.Target = Target; }
  }
  public abstract class Mixin<TARGET, MIXIN> : Mixin<TARGET>, IMixin<TARGET, MIXIN>
    where MIXIN : Mixin<TARGET, MIXIN>
    where TARGET : IHasMixin<TARGET, MIXIN> {
    public Mixin(TARGET Target) : base(Target) { }
  }
  public class MixinManager<TARGET> where TARGET : IHasMixin<TARGET> {
    private static readonly Dictionary<Type, ConstructorInfo> All = new Dictionary<Type, ConstructorInfo>();
    private readonly Dictionary<Type, IMixin<TARGET>> Instances = new Dictionary<Type, IMixin<TARGET>>();
    private readonly HashSet<Type> Initialized = new HashSet<Type>();
    private static bool IsInit = false;
    public IEnumerable<IMixin<TARGET>> Mixins { get { return Instances.Values; } }

    private static void InitMixinManager() {
      if (IsInit) return;
      IsInit = true;
      
      foreach (var intf in typeof(TARGET).GetInterfaces()) {

        if (intf.Name != typeof(IHasMixin<,>).Name) continue;
        if (intf.GetGenericTypeDefinition() != typeof(IHasMixin<,>)) continue;
        //ype t0 = typeof(IHasMixin<TARGET>);
        //intf.IsSubclassOf(typeof(IHasMixin<TARGET>)).Assert();
        var extension = intf.GetGenericArguments()[1];
        extension.IsSubclassOf(typeof(Mixin<TARGET>)).Assert();
        var c = extension.GetConstructor(new Type[] { typeof(TARGET) });
        (c != null).Assert();
        All[extension] = c; // (Extension<TARGET>)c.Invoke(new object[] { });
      }
    }
    public MIXIN Get<MIXIN>() where MIXIN : Mixin<TARGET> {
      return (MIXIN)Instances[typeof(MIXIN)];
    }
    public void ForceInit<MIXIN>(TARGET Target) where MIXIN : Mixin<TARGET> {
      Get<MIXIN>().ForceInit<MIXIN>(Target);
    }
    public MixinManager(TARGET Target) {
      InitMixinManager();
      foreach (var e in All) {
        Instances[e.Key] = (IMixin<TARGET>) e.Value.Invoke(new object[] { Target });
      }
    }
    public void ForceInit(TARGET Target, Type type) {
      if (Initialized.Contains(type) || !All.ContainsKey(type)) return;
      Initialized.Add(type);
      Instances[type].DoInit(Target);
    }
    public virtual void DoInit(TARGET Target) {
      foreach (var e in All) ForceInit(Target, e.Key);
    }
  }
}
